Coverage Report

Created: 2025-05-07 21:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\dynamic\src\component\factory.rs
Line
Count
Source
1
// Copyright (c) 2025, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use std::cell::RefCell;
30
use std::collections::HashMap;
31
use std::hash::{Hash, Hasher};
32
use std::rc::Rc;
33
use crate::component::ComponentType;
34
use bp3d_util::{simple_error, try_opt};
35
use bp3d_protoc::compiler::structure::FixedFieldType;
36
use bp3d_protoc::model::protocol::Endianness;
37
38
simple_error! {
39
    pub Error {
40
        AlreadyRegistered(String) => "factory for component '{}' is already registered",
41
        NotFound(String) => "component name '{}' not found",
42
        Fail(String) => "factory for component name '{}' has failed"
43
    }
44
}
45
46
pub type Result<T> = std::result::Result<T, Error>;
47
48
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
49
pub enum SizeType {
50
    U8,
51
    U16LE,
52
    U16BE,
53
    U32LE,
54
    U32BE,
55
    U64LE,
56
    U64BE
57
}
58
59
impl SizeType {
60
0
    pub fn get_endianness(&self) -> Endianness {
61
0
        match self {
62
0
            SizeType::U16LE | SizeType::U32LE | SizeType::U64LE => Endianness::Little,
63
0
            _ => Endianness::Big
64
        }
65
0
    }
66
67
0
    pub fn get_size(&self) -> usize {
68
0
        match self {
69
0
            SizeType::U8 => 1,
70
0
            SizeType::U16LE | SizeType::U16BE => 2,
71
0
            SizeType::U32LE | SizeType::U32BE => 4,
72
0
            SizeType::U64LE | SizeType::U64BE => 8
73
        }
74
0
    }
75
}
76
77
impl From<(FixedFieldType, Endianness)> for SizeType {
78
0
    fn from((value, endianness): (FixedFieldType, Endianness)) -> Self {
79
0
        match (value, endianness) {
80
0
            (FixedFieldType::Int8 | FixedFieldType::UInt8 | FixedFieldType::Bool, _) => SizeType::U8,
81
0
            (FixedFieldType::Int16 | FixedFieldType::UInt16, Endianness::Little) => SizeType::U16LE,
82
0
            (FixedFieldType::Int16 | FixedFieldType::UInt16, Endianness::Big) => SizeType::U16BE,
83
0
            (FixedFieldType::Int32 | FixedFieldType::UInt32 | FixedFieldType::Float32, Endianness::Little) => SizeType::U32LE,
84
0
            (FixedFieldType::Int32 | FixedFieldType::UInt32 | FixedFieldType::Float32, Endianness::Big) => SizeType::U32BE,
85
0
            (FixedFieldType::Int64 | FixedFieldType::UInt64 | FixedFieldType::Float64, Endianness::Little) => SizeType::U64LE,
86
0
            (FixedFieldType::Int64 | FixedFieldType::UInt64 | FixedFieldType::Float64, Endianness::Big) => SizeType::U64BE
87
        }
88
0
    }
89
}
90
91
pub struct ContainerOptions {
92
    pub inner_ty: Rc<dyn ComponentType>,
93
    pub count_ty: SizeType,
94
    pub size_ty: Option<SizeType>
95
}
96
97
impl ContainerOptions {
98
    #[inline(always)]
99
0
    fn inner_ty_ptr(&self) -> usize {
100
0
        let key = self.inner_ty.key();
101
0
        if key != 0 {
  Branch (101:12): [Folded - Ignored]
  Branch (101:12): [True: 0, False: 0]
102
0
            key
103
        } else {
104
0
            Rc::as_ptr(&self.inner_ty) as *const () as _
105
        }
106
0
    }
107
}
108
109
impl Eq for ContainerOptions {}
110
111
impl PartialEq for ContainerOptions {
112
0
    fn eq(&self, other: &Self) -> bool {
113
0
        self.inner_ty_ptr() == other.inner_ty_ptr() && self.count_ty == other.count_ty && self.size_ty == other.size_ty
  Branch (113:9): [Folded - Ignored]
  Branch (113:56): [Folded - Ignored]
  Branch (113:9): [True: 0, False: 0]
  Branch (113:56): [True: 0, False: 0]
114
0
    }
115
}
116
117
impl Hash for ContainerOptions {
118
0
    fn hash<H: Hasher>(&self, state: &mut H) {
119
0
        state.write_usize(self.inner_ty_ptr());
120
0
        self.count_ty.hash(state);
121
0
        self.size_ty.hash(state);
122
0
    }
123
}
124
125
#[derive(Eq, PartialEq, Hash)]
126
enum CompKeyInner {
127
    Container(ContainerOptions),
128
    Buffer(Option<SizeType>)
129
}
130
131
#[derive(Eq, PartialEq, Hash)]
132
pub struct Key {
133
    name: String,
134
    inner: CompKeyInner
135
}
136
137
impl Key {
138
0
    pub fn for_container(name: impl Into<String>, options: ContainerOptions) -> Self {
139
0
        Self {
140
0
            name: name.into(),
141
0
            inner: CompKeyInner::Container(options)
142
0
        }
143
0
    }
144
145
6
    pub fn for_buffer(name: impl Into<String>, size: Option<SizeType>) -> Self {
146
6
        Self {
147
6
            name: name.into(),
148
6
            inner: CompKeyInner::Buffer(size)
149
6
        }
150
6
    }
151
}
152
153
pub struct Factory {
154
    container_factories: HashMap<String, Box<dyn Fn(&ContainerOptions) -> Option<Rc<dyn ComponentType>>>>,
155
    buffer_factories: HashMap<String, Box<dyn Fn(Option<SizeType>) -> Option<Rc<dyn ComponentType>>>>,
156
    components: RefCell<HashMap<Key, Rc<dyn ComponentType>>>
157
}
158
159
impl Factory {
160
4
    pub fn new() -> Self {
161
4
        Self {
162
4
            container_factories: HashMap::new(),
163
4
            buffer_factories: HashMap::new(),
164
4
            components: RefCell::new(HashMap::new())
165
4
        }
166
4
    }
167
168
0
    pub fn add_container_factory<F: Fn(&ContainerOptions) -> Option<Rc<dyn ComponentType>> + 'static>(&mut self, component_name: impl Into<String>, factory: F) -> Result<()> {
169
0
        let component_name = component_name.into();
170
0
        if self.container_factories.contains_key(&component_name) && self.buffer_factories.contains_key(&component_name) {
  Branch (170:12): [Folded - Ignored]
  Branch (170:70): [Folded - Ignored]
  Branch (170:12): [Folded - Ignored]
  Branch (170:70): [Folded - Ignored]
171
0
            return Err(Error::AlreadyRegistered(component_name));
172
0
        }
173
0
        self.container_factories.insert(component_name.clone(), Box::new(factory));
174
0
        Ok(())
175
0
    }
176
177
0
    pub fn add_buffer_factory<F: Fn(Option<SizeType>) -> Option<Rc<dyn ComponentType>> + 'static>(&mut self, component_name: impl Into<String>, factory: F) -> Result<()> {
178
0
        let component_name = component_name.into();
179
0
        if self.container_factories.contains_key(&component_name) && self.buffer_factories.contains_key(&component_name) {
  Branch (179:12): [Folded - Ignored]
  Branch (179:70): [Folded - Ignored]
  Branch (179:12): [Folded - Ignored]
  Branch (179:70): [Folded - Ignored]
180
0
            return Err(Error::AlreadyRegistered(component_name));
181
0
        }
182
0
        self.buffer_factories.insert(component_name.clone(), Box::new(factory));
183
0
        Ok(())
184
0
    }
185
186
2
    pub fn add_component(&mut self, key: Key, component: impl ComponentType + 'static) -> Result<()> {
187
2
        if self.components.get_mut().contains_key(&key) {
  Branch (187:12): [Folded - Ignored]
  Branch (187:12): [Folded - Ignored]
  Branch (187:12): [True: 0, False: 1]
  Branch (187:12): [True: 0, False: 1]
188
0
            return Err(Error::AlreadyRegistered(key.name));
189
2
        }
190
2
        self.components.get_mut().insert(key, Rc::new(component));
191
2
        Ok(())
192
2
    }
193
194
4
    pub fn with_component<R>(&self, key: Key, f: impl FnOnce(&Rc<dyn ComponentType>) -> R) -> Result<R> {
195
4
        let mut components = self.components.borrow_mut();
196
4
        if let Some(component) = components.get(&key) {
  Branch (196:16): [Folded - Ignored]
  Branch (196:16): [True: 0, False: 0]
  Branch (196:16): [True: 4, False: 0]
  Branch (196:16): [True: 0, False: 0]
  Branch (196:16): [True: 0, False: 0]
  Branch (196:16): [True: 0, False: 0]
  Branch (196:16): [True: 0, False: 0]
197
4
            return Ok(f(&*component));
198
0
        }
199
0
        let ct = match &key.inner {
200
0
            CompKeyInner::Container(opts) => self.container_factories.get(&key.name).map(|v| v(opts)),
201
0
            CompKeyInner::Buffer(opt) => self.buffer_factories.get(&key.name).map(|v| v(*opt))
202
        };
203
0
        let ct = try_opt!(try_opt!(ct => Error::NotFound(key.name)) => Error::Fail(key.name));
204
0
        let component = components.entry(key).or_insert(ct);
205
0
        Ok(f(component))
206
4
    }
207
208
0
    pub fn get_component(&self, key: Key) -> Result<Rc<dyn ComponentType>> {
209
0
        self.with_component(key, |c| c.clone())
210
0
    }
211
}